#include "Holdout.h"

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Holdout
 *      Method:  Holdout
 * Description:  Divide o conjunto de dados em 2 sub-amostras
 *--------------------------------------------------------------------------------------
 */
Holdout::Holdout(DataSet& cDS) :
    ds(cDS)
{
    group.resize(ds.size());

    // Contador de dados por grupo
    vuint count;
    count.resize(2);

    // Quantidade ideal de dados para o grupo de treinamento
    uint gTrainSize = round((2 * ds.size()) / (double) 3);
    // Quantidade ideal de dados para o grupo de teste
    uint gTestSize = round(ds.size() / (double) 3);

    // Iniciliza o contador
    count[0] = 0;
    count[1] = 0;

    // Para cada entrada/saída
    for(uint i = 0; i < ds.size(); i++)
    {
        // Escolhe um grupo aleatoriamente
        uint r = rand() % 3;

        // Caso escolheu o grupo de treinamento
        if(r == 0 || r == 1)
        {
            if(count[0] < gTrainSize)
                count[0]++, group[i] = 0;
            else
                count[1]++, group[i] = 1;
        }

        // Caso escolheu o grupo de teste
        else if(r == 2)
        {
            if(count[1] < gTestSize)
                count[1]++, group[i] = 1;
            else
                count[0]++, group[i] = 0;
        }
    }
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Holdout
 *      Method:  fillDataSets
 * Description:  Preenche os dados de treinamento e de teste para uma certa iteração
 *--------------------------------------------------------------------------------------
 */
void Holdout::fillDataSets(DataSet& training, DataSet& test)
{
    for(uint j = 0; j < ds.size(); j++)
    {
        if(group[j] == 0)
            training.addData(ds.get(j));
        else
            test.addData(ds.get(j));
    }
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Holdout
 *      Method:  execute
 * Description:  Executa o algoritmo de CrossValidation para o perceptron passado
 *--------------------------------------------------------------------------------------
 */
void Holdout::execute(MLP& mlp, double tolerance, uint maxIt)
{
    double trainingTime = 0, testTime = 0;

    DataSet training(ds.getNInVars(), ds.getNOutVars());
    DataSet test(ds.getNInVars(), ds.getNOutVars());

    // Divide as subamostras em amostras de treinamento e teste
    fillDataSets(training, test);

    training.print();
    test.print();

    mlp.train(training, tolerance, maxIt);
    trainingTime += training.getTime();

    mlp.test(test, tolerance);
    testTime += test.getTime();

    double avgRate = test.getSuccessRate();

    cout << "TRAINING SUCCESS RATE = " << training.getSuccessRate() << endl;
    cout << "TEST SUCCESS RATE = " << test.getSuccessRate() << endl;
    cout << "NUMBER OF TRAINING ITERATIONS = " << training.getNIterations() << endl;
    cout << "TOTAL TRAINING TIME = " << trainingTime << " msec" << endl;
    cout << "TOTAL TEST TIME = " << testTime << " msec" << endl << endl;

    ds.setSuccessRate(avgRate);
    ds.setTime(trainingTime + testTime);
    ds.setNIterations(training.getNIterations());
}

